package com.godenwater.recv.server.sl651;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import cn.gov.mwr.sl651.HydroMessage;
import cn.gov.mwr.sl651.IMessageBody;
import cn.gov.mwr.sl651.IMessageHeader;
import cn.gov.mwr.sl651.Symbol;
import cn.gov.mwr.sl651.body.UpBaseBody;
import cn.gov.mwr.sl651.utils.ByteUtil;

/**
 * 报文解码类，作为上行报文的解码类，此类必须实现对所有消息体的封闭细节
 * 
 * @ClassName: YfServerDataDecoder
 * @Description: 处理断包和粘包的实现类
 * @author lipujun
 * @date Mar 2, 2013
 * 
 */
public class ServerDataDecoder2 extends CumulativeProtocolDecoder {
	private static final AttributeKey BUF_BYTE = new AttributeKey(
			ServerDataDecoder2.class, "BUF_KEY");

	private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context");

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	public Context getContext(IoSession session) {
		Context ctx = (Context) session.getAttribute(CONTEXT);
		if (ctx == null) {
			ctx = new Context();
			session.setAttribute(CONTEXT, ctx);
		}
		return ctx;
	}

	private class Context {
		// 状态变量
		private final IoBuffer innerBuffer;

		public Context() {
			innerBuffer = IoBuffer.allocate(100).setAutoExpand(true);
		}
	}

	/**
	 * 这个办法的返回值是重点：
	 * 
	 * 1、当内容正好时，返回false，告诉父类可以进行下一批消息的处理
	 * 
	 * 2、内容不符时，需要下一批消息的内容，此时返回false
	 * ，如许父类会将内容放进IoSession中，等下次数据来后就主动拼装再交给本类的doDecode
	 * 
	 * 3、当内容多时，返回true，因为需要再将本批数据进行读取，父类会将残剩的数据再次推送本类的doDecode
	 */
	@Override
	protected boolean doDecode(IoSession session, IoBuffer buffer,
			ProtocolDecoderOutput out) throws Exception {
		// TODO Auto-generated method stub
		logger.info(">> HEX : "
				+ StringUtils.trimAllWhitespace(buffer.getHexDump()));

		// logger.info(">> HEX : " +
		// ByteUtil.toHexString(buffer.buf().array()));

		int size = 0;
		int rcrlen = 0;
		int bodySizeLen = 0;

		if (buffer.remaining() > 0) {// 表示缓冲区中有数据
			// System.out.println("------buffer.remaining() ---------"
			// + buffer.remaining() );

			buffer.mark();// 标记当前位置，以便reset

			byte[] mode = new byte[1];
			buffer.get(mode);

			IMessageHeader header;
			if (mode[0] == (byte) Symbol.SOH_ASC) {
				header = new cn.gov.mwr.sl651.header.AscHeader();
				header.setStartBit(new byte[] { mode[0] });

				// 上行的，中心站地址在前
				byte[] centerAddr = new byte[header.getCenterAddrLen()];
				buffer.get(centerAddr);
				// String hexCenterAddr = new String(centerAddr);
				// header.setCenterAddr(ByteUtil.HexStringToBinary(hexCenterAddr));
				header.setCenterAddr(centerAddr);

				// 上行的，遥测站地址在前
				byte[] stationAddr = new byte[header.getStationAddrLen()];
				buffer.get(stationAddr);
				// String hexStationAddr = new String(stationAddr);
				// header.setStationAddr(ByteUtil
				// .HexStringToBinary(hexStationAddr));
				header.setStationAddr(stationAddr);

				byte[] password = new byte[header.getPasswordLen()];
				buffer.get(password);
				// String hexPassword = new String(password);
				// header.setPassword(ByteUtil.HexStringToBinary(hexPassword));
				header.setPassword(password);

				byte[] funcCode = new byte[header.getFuncCodeLen()];
				buffer.get(funcCode);
				// String hexFuncCode = new String(funcCode);
				// header.setFuncCode(ByteUtil.HexStringToBinary(hexFuncCode));
				header.setFuncCode(funcCode);

				byte[] BodySize = new byte[header.getBodySizeLen()];
				buffer.get(BodySize);
				String hexBodySizeStr = new String(BodySize);
				byte[] hexBodySize = ByteUtil
						.HexStringToBinary(hexBodySizeStr);
				// header.setBodySize(hexBodySize);
				header.setBodySize(BodySize);

				byte[] bodyStartBit = new byte[header.getBodyStartBitLen()];
				buffer.get(bodyStartBit);
				// String hexBodyStartBit = new String(bodyStartBit);
				// header.setBodyStartBit(bodyStartBit);
				header.setBodyStartBit(bodyStartBit);
				// System.out.println(">> hexBodyStartBit " +
				// ByteUtil.toHexString(bodyStartBit));

				if (header.getBodyStartBit()[0] == Symbol.SYN) {
					byte[] bodyCount = new byte[header.getBodyCountLen()];
					buffer.get(bodyCount);
					// String hexBodyCount = new String(BodySize);
					// header.setBodyCount(ByteUtil.HexStringToBinary(hexBodyCount));
					header.setBodyCount(bodyCount);
				}

				// 转换报文中的上下行标识及报文长度
				bodySizeLen = parseLength(hexBodySize);// 需要考虑拆分字节;
				// System.out.println("数据字节长度  " + bodySizeLen);
				if (bodyStartBit[0] == Symbol.SYN) {
					bodySizeLen = bodySizeLen - header.getBodyCountLen();
				}
				rcrlen = 4;

			} else if (mode[0] == (byte) Symbol.SOH_HEX) {

				byte[] startBit = new byte[1];
				buffer.get(startBit);

				//表示是7474，水文的16进制报文数据
				if (startBit[0] == (byte) Symbol.SOH_HEX) {

					header = new cn.gov.mwr.sl651.header.HexHeader();

					header.setStartBit(new byte[] { Symbol.SOH_HEX,
							Symbol.SOH_HEX });

					// 上行的，中心站地址在前
					byte[] centerAddr = new byte[header.getCenterAddrLen()];
					buffer.get(centerAddr);
					header.setCenterAddr(centerAddr);

					// 上行的，遥测站地址在后
					byte[] stationAddr = new byte[header.getStationAddrLen()];
					buffer.get(stationAddr);
					header.setStationAddr(stationAddr);

					// 解析密码
					byte[] password = new byte[header.getPasswordLen()];
					buffer.get(password);
					header.setPassword(password);

					// 解析 功能码
					byte[] funcCode = new byte[header.getFuncCodeLen()];
					buffer.get(funcCode);
					header.setFuncCode(funcCode);

					// 解析报文上行标识及消息体长度
					byte[] BodySize = new byte[header.getBodySizeLen()];
					buffer.get(BodySize);
					header.setBodySize(BodySize);

					// 解析消息开始体
					byte[] bodyStartBit = new byte[header.getBodyStartBitLen()];
					buffer.get(bodyStartBit);
					header.setBodyStartBit(bodyStartBit);

					// 如果是SYN，表示是多包发送。多包发送，一次确认的传输模式中使用
					if (bodyStartBit[0] == Symbol.SYN) {
						byte[] bodyCount = new byte[header.getBodyCountLen()];
						buffer.get(bodyCount);
						header.setBodyCount(bodyCount);
					}

					// 转换报文中的上下行标识及报文长度
					bodySizeLen = parseLength(BodySize);// 需要考虑拆分字节;
					if (bodyStartBit[0] == Symbol.SYN) {
						bodySizeLen = bodySizeLen
								- header.getBodyCountLen();
					}
					rcrlen = (mode[0] == Symbol.SOH_ASC) ? 4 : 2;

				}else{
					//燕禹协议
					header = new cn.gov.mwr.sl651.header.HexHeader();
					
					//7E  目的地址  源地址  特征 长度 时间1《数据》 ETX  CRC16H  CRC16L  
					header.setStartBit(new byte[] { Symbol.SOH_HEX});
					// 上行的，中心站地址在前
					header.setCenterAddr(startBit);
					
					//源地址,测站地址
					byte[] stationAddr = new byte[header.getStationAddrLen()];
					buffer.get(stationAddr);
					header.setStationAddr(stationAddr);
					
					//特征码
					byte[] funccode = new byte[header.getFuncCodeLen()];
					buffer.get(funccode);
					header.setFuncCode(funccode);

					//报文长度
					byte[] bodySize = new byte[header.getBodySizeLen()];
					buffer.get(bodySize);
					header.setBodySize(bodySize); 
					
					//时间长度
//					byte[] bodySize = new byte[header.get()];
//					buffer.get(bodySize);
//					header.setBodySize(bodySize); 
//					
//				 
					// 转换报文中的上下行标识及报文长度
					bodySizeLen = ByteUtil.bytesToInt(bodySize);// 需要考虑拆分字节;
					
					rcrlen =   2;
					
				}

			} else if (mode[0] == (byte) 0x24) {
				// 在此处再添加一个对卫星数据进行过程的地方
				// 24545853432C642C3133343431352C37382C
				logger.info(">> 接收北斗卫星消息 ");

				// 需要跳过18个字节
				byte[] BD = new byte[17];
				buffer.get(BD);

				// System.out.println(">> 接收北斗卫星消息    " +
				// ByteUtil.toHexString(BD));

				// -------------------------------------
				// 以上为卫星的操作
				// -------------------------------------

				header = new cn.gov.mwr.sl651.header.HexHeader();
				byte[] startBit = new byte[2];
				buffer.get(startBit);
				header.setStartBit(new byte[] { Symbol.SOH_HEX, Symbol.SOH_HEX });

				// 上行的，中心站地址在前
				byte[] centerAddr = new byte[header.getCenterAddrLen()];
				buffer.get(centerAddr);
				header.setCenterAddr(centerAddr);

				// 上行的，遥测站地址在前
				byte[] stationAddr = new byte[header.getStationAddrLen()];
				buffer.get(stationAddr);
				header.setStationAddr(stationAddr);

				byte[] password = new byte[header.getPasswordLen()];
				buffer.get(password);
				header.setPassword(password);
				//
				byte[] funcCode = new byte[header.getFuncCodeLen()];
				buffer.get(funcCode);
				header.setFuncCode(funcCode);

				byte[] BodySize = new byte[header.getBodySizeLen()];
				buffer.get(BodySize);
				header.setBodySize(BodySize);

				byte[] bodyStartBit = new byte[header.getBodyStartBitLen()];
				buffer.get(bodyStartBit);
				header.setBodyStartBit(bodyStartBit);

				if (bodyStartBit[0] == Symbol.SYN) {
					byte[] bodyCount = new byte[header.getBodyCountLen()];
					buffer.get(bodyCount);
					header.setBodyCount(bodyCount);
				}

				// 转换报文中的上下行标识及报文长度
				bodySizeLen = parseLength(BodySize);// 需要考虑拆分字节;
				if (bodyStartBit[0] == Symbol.SYN) {
					bodySizeLen = bodySizeLen - header.getBodyCountLen();
				}
				rcrlen = (mode[0] == Symbol.SOH_ASC) ? 4 : 2;

			} else {

				// 处理DTU设备上线时的登录信息，发的是DTU编号和手机号
				// buffer.get(buffer.array().length);
				System.out.println(">> 非协议报文 ");
				byte[] temp = new byte[buffer.remaining()];
				buffer.get(temp);
				return true;
			}

			size = bodySizeLen + 1 + rcrlen;

			// 若是消息内容的长度不敷则直接返回true
			if (size > buffer.remaining()) {// 若是消息大小与缓冲区中的内容大小不匹配，则重置，相当于不读取size
				// System.out.println("------size > buffer.remaining() ---------"
				// + size + "  " + buffer.remaining());
				buffer.reset();
				return false;// 接管新数据，以拼凑成完全数据
			} else {
				// System.out
				// .println("------size < buffer.remaining() ---------BodySizeLen "
				// + BodySizeLen
				// + " size "
				// + size
				// + "  "
				// + buffer.remaining());
				byte[] bodyContent = new byte[bodySizeLen];// 根据前面header的内容来获取长度
				buffer.get(bodyContent);

				// 终止符
				byte[] eof = new byte[1];

				// 校验码
				byte[] crc = new byte[rcrlen];

				// 两种协议的CRC校验符与结束符，正好相反
				if ((byte) header.getStartBit()[0] == (byte) 0x68) {
					// 水资源协议
					buffer.get(crc);
					buffer.get(eof);
				} else {
					// 水文协议
					buffer.get(eof);
					buffer.get(crc);
				}

				// 组装消息
				HydroMessage message = new HydroMessage();
				message.setHeader(header);

				// 根据功能码，构建body体
				IMessageBody body = new UpBaseBody();
				body.setContents(bodyContent);
				message.setBody(body);
				message.setEOF(eof[0]);
				message.setCRC(crc);

				out.write(message);

				if (mode[0] == (byte) 0x24) {
					// buffer.reset();
					// buffer.clear();
					// 提取后续的字节，不做处理，直接当垃圾
					byte[] remain = new byte[buffer.remaining()];
					buffer.get(remain);
					logger.info("+++++++++++++++++++++++++++++++++++++++++++++++++++++++ 【"
							+ remain.length
							+ " _ "
							+ buffer.remaining()
							+ " _ " + size + "】");
					return false;
				}

				if (buffer.remaining() > 0) {// 若是读取内容后还粘了包，就让父类再给解析一次，返回true进行下一次解析
					return true;
				}
			}
		}
		return false;// 处理重发成功，让父类进行接管下个包
	}

	private int parseLength(byte[] bodyLen) {
		byte A1 = (byte) (bodyLen[0] & 0x0f);
		byte A2 = bodyLen[1];
		return ByteUtil.bytesToUshort(new byte[] { A1, A2 });
	}

	public void dispose(IoSession session) throws Exception {
		// TODO Auto-generated method stub

	}

	public void finishDecode(IoSession session, ProtocolDecoderOutput out)
			throws Exception {
		// TODO Auto-generated method stub

	}

}
